/*
 * Created on Aug 4, 2006
 *
 */
package com.inibb.app.sftp;

import java.io.File;
import java.net.InetAddress;
import java.util.Properties;
import java.util.StringTokenizer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.inibb.app.sftp.exception.SFTPBaseException;
import com.inibb.app.sftp.exception.SFTPConnectionPoolException;







/**
 * @author PCCW
 *
 */
public class SFTPConnectionPoolMapHolder  {
	
	private static final Logger LOG = LoggerFactory.getLogger(SFTPConnectionPool.class);

	
	public static final String KEY_ESERVICES = "eservice";

	public static final String CONFIG_BASE = "common.ssh";
	
	public static final String PARAM_OTHER_CONN = "pool.otherConn";
	
	public static final String PARAM_SECTIONS = "pool.sections";
	public static final String PARAM_MAX_TRY_COUNT = "pool.maxTryCount";
	public static final String PARAM_TRY_WAIT = "pool.tryWait";
	public static final String PARAM_SFTPIMPL_CLASS = "pool.sftpImpl";
	public static final String PARAM_LOCAL_SFTPIMPL_CLASS = ".sftpImpl";
	public static final String PARAM_POOLSIZE = ".poolSize";
	
	public static final String PARAM_OTH_LOGIN = ".loginname";
	public static final String PARAM_OTH_PASSWORD = ".passwd";
	public static final String PARAM_OTH_HOST = ".host";
	public static final String PARAM_OTH_PORT = ".port";
	public static final String PARAM_OTH_KEYNAME = ".keyname";
	
	private static final String PARAM_TSS_NONE_CIPHER = "tss.noneCipherMode";
	private static final String PARAM_OTH_NONE_CIPHER = ".noneCipherMode";
	
	
	private static SFTPConnectionPoolMap map = null;
	private int maxTryCount = 0;
	private int tryWait = -1;
	private String sftpImpl = null;
	private Properties config = null;
	
	public SFTPConnectionPoolMapHolder(Properties config) {
		super();
		this.config = config;
	}

	/**
	 * Get the SFTPConnectionPoolMap
	 * @return
	 */
	public static SFTPConnectionPoolMap getMap() {
		return map;
	}
	
	/* 
	 * @see com.pccw.framework.servlet.IAutoStart#init()
	 */
	public void init() throws SFTPBaseException, SFTPConnectionPoolException {
		
		LOG.info("Initialize sftpPool starts ...");
		//TODO: KOBE: please modify it
		//config = System.getProperties();
		String currParam = null;
		if (map!=null) {
			map.disconnectAll();
			map = null;
		}
		
		map = new SFTPConnectionPoolMap();
		
		try {
			
			currParam = CONFIG_BASE+"."+PARAM_MAX_TRY_COUNT;
			String _maxTryCount = getProperty(currParam);
			if (_maxTryCount!=null) {
				maxTryCount = Integer.parseInt(_maxTryCount);
			} else {
				throw new IllegalArgumentException(currParam+" undefined.");
			}
			
			currParam = CONFIG_BASE+"."+PARAM_TRY_WAIT;
			String _tryWait = getProperty(currParam);
			if (_tryWait!=null) {
				tryWait = Integer.parseInt(_tryWait);
			} else {
				throw new IllegalArgumentException(currParam+" undefined.");
			}

			int tssPoolSize = 0;
			currParam = CONFIG_BASE+"."+PARAM_SECTIONS+PARAM_POOLSIZE;
			String _poolSize = getProperty(currParam);
			if (_poolSize!=null) {
				tssPoolSize = Integer.parseInt(_poolSize);
			} else {
				throw new IllegalArgumentException(currParam+" undefined.");
			}

			currParam = CONFIG_BASE+"."+PARAM_SFTPIMPL_CLASS;
			sftpImpl = getProperty(currParam);
			
			currParam = CONFIG_BASE+"."+PARAM_TSS_NONE_CIPHER;
			boolean noneCipherMode = "true".equalsIgnoreCase(getProperty(currParam));

			currParam = CONFIG_BASE+"."+PARAM_SECTIONS;
			String _sections = getProperty(currParam);
			StringTokenizer stz= null;
			if (_sections!=null) {
				stz = new StringTokenizer(_sections,";");
				String currSection = null;
				while (stz.hasMoreTokens()) {
				    try {
						currSection = stz.nextToken();
						LOG.info("Initializing SFTPConnectionPool for section : "+currSection);
						String _addr = SectionInfo.getTSSHostAddr(currSection);
						if (_addr==null) throw new IllegalArgumentException("Undefined address for section : "+currSection);
						
						InetAddress addr = InetAddress.getByName(_addr);
						int port = SectionInfo.getSShTSSPort(currSection);
						String username = SectionInfo.getSShTSSUserName(currSection) ;
						if (username==null) throw new IllegalArgumentException("Undefined loginname for section : "+currSection);
						
						String _password = SectionInfo.getSShTSSPassword(currSection);
						char[] password = null;
						if (_password!=null) password = _password.toCharArray();
						File keyFile = SectionInfo.getSShTSSKeyName(currSection);
						if (password==null && keyFile==null) {
							throw new IllegalArgumentException("Both password and keyFile is not defined for section : "+currSection);
						}
						
						int currPoolSize = tssPoolSize;
						currParam = CONFIG_BASE+"."+PARAM_SECTIONS+PARAM_POOLSIZE+"."+currSection;
						String _sectPoolSize =  getProperty(currParam);
						if (_sectPoolSize!= null) currPoolSize = Integer.parseInt(_sectPoolSize);
						
						SFTPConnectionPool pool = initPool(currSection,addr,port,username,keyFile,password,currPoolSize,noneCipherMode,sftpImpl);
						map.putPool(currSection,pool);
						LOG.info("Initialization of SFTPConnectionPool for section : "+currSection+" succeed!");
				    } catch(Exception e) {
				        LOG.error("Error when initializing sftp connection to "+currSection,e);
				        LOG.error("SFTP","Error when initializing sftp connection to "+currSection);
				    }
				}
			} else {
				LOG.warn("No sections defined for SFTPConnectionPool.");
			}
			
			currParam = CONFIG_BASE+"."+PARAM_OTHER_CONN;
			String _otherConn = getProperty(currParam);
			String currConn = null;
			if (_otherConn!=null) {
				stz = new StringTokenizer(_otherConn,";");
				while (stz.hasMoreTokens()) {
				    try {
						currConn = stz.nextToken();
						LOG.info("Initializing SFTPConnectionPool for other connection : "+currConn);
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_HOST;
						String _addr = getProperty(currParam);
						if (_addr==null) throw new IllegalArgumentException("Undefined address for "+currConn);
						InetAddress addr = InetAddress.getByName(_addr);

						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_PORT;
						String _port = getProperty(currParam);
						int port = 22;
						if (_port!=null) port = Integer.parseInt(_port);

						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_LOGIN;
						String username = getProperty(currParam);
						if (username==null) throw new IllegalArgumentException("Undefined loginname for "+currConn);
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_PASSWORD;
						String _password = getProperty(currParam);
						char[] password = null;
						if (_password!=null) password = _password.toCharArray();
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_LOCAL_SFTPIMPL_CLASS;
						String local_sftpImpl = getProperty(currParam);
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_KEYNAME;
						String _keyFile = getProperty(currParam);
						File keyFile = null;
						if (_keyFile!=null) keyFile = new File(_keyFile);
						if (password==null && keyFile==null) {
							throw new IllegalArgumentException("Both password and keyFile is not defined for "+currConn);
						}
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_OTH_NONE_CIPHER;
						boolean othNoneCipherMode = "true".equalsIgnoreCase(getProperty(currParam));
						
						currParam = CONFIG_BASE+"."+currConn+PARAM_POOLSIZE;
						String _othPoolSize = getProperty(currParam);
						int othPoolSize = 0;
						if (_othPoolSize!=null) {
							othPoolSize = Integer.parseInt(_othPoolSize);
						} else {
							throw new IllegalArgumentException(currParam+" undefined.");
						}
						
						
						SFTPConnectionPool pool = initPool(currConn,addr,port,username,keyFile,password,othPoolSize,othNoneCipherMode,local_sftpImpl);
						map.putPool(currConn,pool);
						
						LOG.info("Initialization of SFTPConnectionPool for "+currConn+" succeed!");
				    } catch(Exception e) {
				        LOG.error("Error when initializing sftp connection to "+currConn,e);
				        LOG.error("SFTP","Error when initializing sftp connection to "+currConn);
				    }
				}
			} else {
				LOG.warn("No other connections defined for SFTPConnectionPool.");
			}
			
			LOG.info("Initialize sftpPool succeed!");
			String[] conns = map.getDefinedPools();
			
			if (conns!=null && conns.length>0) {
				StringBuffer connbuf = new StringBuffer();
				for (int i=0;i<conns.length;i++) {
					if (i>0)connbuf.append(',');
					connbuf.append(conns[i]);
				}
				LOG.info("Available sftp pools are ["+connbuf.toString()+"]");
			} else {
				LOG.info("No sftp pools defined.");
			}
			
		} catch (Exception e) {
			throw new SFTPConnectionPoolException(SFTPConnectionPoolException.CODE_INIT,"Error initializing SFTPConnectionPoolMapHolder in param : "+currParam,e);
		}

	}
	
	private String getProperty(String key) {
		return getProperty(key,null);
	}	
	
	private String getProperty(String key, String defaultV) {
		String results = config.getProperty(key,defaultV);
		LOG.debug("param:"+key+"="+results);
		return results;
	}
	
	private SFTPConnectionPool initPool(String name, InetAddress addr, int port, String username, File keyFile, char[] password, int poolSize, boolean noneCipherMode, String local_sftpImpl) 
		throws SFTPConnectionPoolException
	{
		SFTPAuthInfo auth = new SFTPAuthInfo(username,keyFile,password);
		SFTPConnectionPool pool = new SFTPConnectionPool();
		pool.setAddr(addr);
		pool.setPort(port);
		pool.setAuth(auth);
		pool.setMaxTryCount(maxTryCount);
		pool.setTryWait(tryWait);
		pool.setPoolSize(poolSize);
		pool.setName(name);
		pool.setNoneCipherMode(noneCipherMode);
		if (local_sftpImpl!=null) pool.setImplClassName(local_sftpImpl);
		else if (sftpImpl!=null) pool.setImplClassName(sftpImpl);
		pool.init();
		return pool;
	}

	/* 
	 * @see com.pccw.framework.servlet.IAutoStart#start()
	 */
	public void start() throws SFTPBaseException {

	}

	/* 
	 * @see com.pccw.framework.servlet.IAutoStart#stop()
	 */
	public void stop() throws SFTPBaseException {
		LOG.info("Stopping sftpPool ...");
		if (map!=null) {
			map.destroy();
		}

	}

	/* 
	 * @see com.pccw.framework.servlet.IAutoStart#destroy()
	 */
	public void destroy() throws SFTPBaseException {
		map = null;
	}

}
